package main

import (
	"fmt"
	"lnzw/engine/aoi/crosslinks"
)

type UnitType int

const (
	Player  UnitType = iota
	Monster UnitType = iota
	NPC     UnitType = iota
	Item    UnitType = iota
)

type Map struct {
	crosslinks.Grid
	width, height int
	units         map[int64]IUnit
}

func (m *Map) GetUnit(id int64) IUnit {
	return m.units[id]
}

func NewMap(width, height int) *Map {
	return &Map{Grid: *crosslinks.NewGrid(width, height), width: width, height: height, units: make(map[int64]IUnit)}
}

func (m *Map) EnterMap(unit IUnit) {
	m.AddEntity(unit.(crosslinks.Entity))
	m.units[unit.GetUnitId()] = unit
	unit.OnEnterMap(m)
}

func (m *Map) LeaveMap(unit IUnit) {
	m.RemoveEntity(unit.(crosslinks.Entity))
	delete(m.units, unit.GetUnitId())
}

func (m *Map) MoveTo(unit IUnit, x, y int) {
	m.MoveEntity(unit.(crosslinks.Entity), crosslinks.Point{X: float32(x), Y: float32(y)})
}

func (b *Map) Watch(u IUnit) []IUnit {
	return b.WatchFree(u, 1)
}

func (b *Map) WatchFree(u IUnit, rangew int) []IUnit {
	entitiesInRange := b.Grid.GetEntitiesInRange(u, rangew)
	units := make([]IUnit, 0, len(entitiesInRange))
	for _, entity := range entitiesInRange {
		if unit, ok := entity.(IUnit); ok {
			if unit.GetUnitId() == u.GetUnitId() {
				continue
			}
			units = append(units, unit)
		}
	}
	return units
}

type IUnit interface {
	crosslinks.Entity
	GetUnitType() UnitType
	GetUnitId() int64
	GetPosition() crosslinks.Point
	OnEnterMap(*Map)
}

type BaseUnit struct {
	crosslinks.AOIEntity
	Id     int64
	NowMap *Map
}

func (b *BaseUnit) GetUnitId() int64 {
	return b.Id
}

func (b *BaseUnit) GetUnitType() UnitType {
	panic("implement me")
}

func (b *BaseUnit) GetPosition() crosslinks.Point {
	return b.Pos
}

func (b *BaseUnit) OnEnterMap(m *Map) {
	b.NowMap = m
}

type PlayerUnit struct {
	BaseUnit
}

type MonsterUnit struct {
	BaseUnit
}

func (p *PlayerUnit) GetUnitType() UnitType {
	return Player
}

func (m *MonsterUnit) GetUnitType() UnitType {
	return Monster
}

func NewPlayerUnit(id int64, x int, y int) *PlayerUnit {
	return &PlayerUnit{
		BaseUnit: BaseUnit{
			AOIEntity: crosslinks.AOIEntity{Pos: crosslinks.Point{X: float32(x), Y: float32(y)}},
			Id:        id,
		},
	}
}

func NewMonsterUnit(id int64, x int, y int) *MonsterUnit {
	return &MonsterUnit{
		BaseUnit: BaseUnit{AOIEntity: crosslinks.AOIEntity{Pos: crosslinks.Point{X: float32(x), Y: float32(y)}}},
	}
}

func main() {
	nmap := NewMap(10, 10)

	me := NewPlayerUnit(1, 5, 5)
	entities := []IUnit{
		me,
		NewPlayerUnit(2, 6, 6),
		NewMonsterUnit(3, 4, 4),
		NewMonsterUnit(4, 8, 8), // 超出范围
	}

	for _, e := range entities {
		nmap.EnterMap(e)
	}

	// 查询 (5, 5) 周围 1 格内的实体
	//entitiesInRange := grid.GetEntitiesInRange(int(me.Pos.X), int(me.Pos.Y), 1, 1)
	nowWatchUnits := me.NowMap.Watch(me)
	fmt.Println("Entities in range before move:")
	for _, e := range nowWatchUnits {
		pos := e.GetPosition()
		fmt.Printf("type:%d,Entity ID: %d, Position: (%.1f, %.1f)\n", e.GetUnitType(), e.GetUnitId(), pos.X, pos.Y)
	}

	// 移动实体 1 到 (7, 7)
	nmap.MoveTo(me, 7, 7)
	// 查询 (7, 7) 周围 1 格内的实体
	nowWatchUnits = me.NowMap.Watch(me)
	fmt.Println("Entities in range after move:")
	for _, e := range nowWatchUnits {
		pos := e.GetPosition()
		fmt.Printf("type:%d, Entity ID: %d, Position: (%.1f, %.1f)\n", e.GetUnitType(), e.GetUnitId(), pos.X, pos.Y)
	}
}
